package com.hyou.sc.zuul.filter.pre;

import java.io.Serializable;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;

import com.hyou.sc.zuul.config.web.WebFilterProp;
import com.hyou.sc.zuul.consts.HeaderConst;
import com.hyou.sc.zuul.dto.form.AuthForm;
import com.hyou.sc.zuul.feign.service.AuthService;
import com.hyou.sc.zuul.util.ZuulUtil;
import com.hyou.springboot.base.ResponseMessage;
import com.hyou.springboot.util.JwtUtil;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.SignatureException;

/**
 * WEB请求适用，前置过滤器。用于请求鉴权处理
 * 
 * @author FengChangshuo
 * @version 1.0.0 2018-04-19 17:04:49 初始创建
 */
public class WebPreAuthFilter extends ZuulFilter {
    
    private static final Logger log = LoggerFactory.getLogger(WebPreAuthFilter.class);
    
    private PathMatcher pathMatcher;
    
    @Autowired
    private WebFilterProp webFilterProp;
    
    @Autowired
    private AuthService authService;
    
    public WebPreAuthFilter() {
        pathMatcher = new AntPathMatcher();
    }
    
    /**
     * <pre>
     * 过滤器的类型
     * pre: 在请求被路由之前调用
     * routing: 在路由请求时被调用
     * post: 在routing和error过滤器之后被调用
     * error: 处理请求时发生错误时被调用
     * </pre>
     * 
     * @return 过滤器的类型
     */
    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }
    
    /**
     * @return 过滤器的执行顺序，数值越小优先级越高。
     */
    @Override
    public int filterOrder() {
        return this.webFilterProp.getPreAuthOrder();
    }
    
    /**
     * 判断该过滤器是否要执行。
     * 
     * @return true : 表示对客户端的请求执行该过滤器。
     */
    @Override
    public boolean shouldFilter() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        String path = request.getServletPath();
        
        log.debug("Path : {}", path);
        
        for (String item : this.webFilterProp.getFreeAuthUrls()) {
            if (pathMatcher.match(item, path)) {
                return false;
            }
        }
        
        return true;
    }
    
    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        
        // 检测是否有Token
        String token = request.getHeader(HeaderConst.TOKEN_HEARDER_NAME);
        if (StringUtils.isBlank(token)) {
            ZuulUtil.sendErrorMessage(ctx, "Token缺失");
            return false;
        }
        
        try {
            Claims claims = JwtUtil.parseJwtToken(token, this.webFilterProp.getJwtSecretKey());
            
            String uid = claims.get("uid", String.class);
            String path = request.getServletPath();
            
            AuthForm form = new AuthForm();
            form.setUid(uid);
            form.setUrl(path);
            ResponseMessage<Serializable> authResMsg = authService.auth(form);
            
            log.debug("form : {}, authResMsg : {}", form, authResMsg);
            return true;
        } catch (ExpiredJwtException e) {
            log.warn("ExpiredJwtException : token : {}, exception : {}", token, e.getMessage());
            ZuulUtil.sendErrorMessage(ctx, "Token过期");
            return false;
        } catch (SignatureException e) {
            log.warn("SignatureException : token : {}, exception : {}", token, e.getMessage());
            ZuulUtil.sendErrorMessage(ctx, "Token验签失败");
            return false;
        } catch (Exception e) {
            log.error("token exception : {}", token);
            log.error(e.getMessage(), e);
            ZuulUtil.sendErrorMessage(ctx, "Token验签失败");
            return false;
        }
    }
    
}
